home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / Delphi 3.0 / DATA.Z / cgiapp.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-01-29  |  15.8 KB  |  573 lines

  1. unit CGIApp;
  2.  
  3. interface
  4.  
  5. uses Windows, Classes, HTTPApp, IniFiles;
  6.  
  7. type
  8.   TCGIRequest = class(TWebRequest)
  9.   private
  10.     FContent: string;
  11.   protected
  12.     function GetStringVariable(Index: Integer): string; override;
  13.     function GetDateVariable(Index: Integer): TDateTime; override;
  14.     function GetIntegerVariable(Index: Integer): Integer; override;
  15.   public
  16.     constructor Create;
  17.     function GetFieldByName(const Name: string): string; override;
  18.     function ReadClient(var Buffer; Count: Integer): Integer; override;
  19.     function ReadString(Count: Integer): string; override;
  20.     function TranslateURI(const URI: string): string; override;
  21.     function WriteClient(var Buffer; Count: Integer): Integer; override;
  22.     function WriteString(const AString: string): Boolean; override;
  23.   end;
  24.  
  25.   TCGIResponse = class(TWebResponse)
  26.   private
  27.     FStatusCode: Integer;
  28.     FStringVariables: array[0..MAX_STRINGS - 1] of string;
  29.     FIntegerVariables: array[0..MAX_INTEGERS - 1] of Integer;
  30.     FDateVariables: array[0..MAX_DATETIMES - 1] of TDateTime;
  31.     FContent: string;
  32.     FSent: Boolean;
  33.   protected
  34.     function GetContent: string; override;
  35.     function GetDateVariable(Index: Integer): TDateTime; override;
  36.     function GetIntegerVariable(Index: Integer): Integer; override;
  37.     function GetLogMessage: string; override;
  38.     function GetStatusCode: Integer; override;
  39.     function GetStringVariable(Index: Integer): string; override;
  40.     function Sent: Boolean; override;
  41.     procedure SetContent(const Value: string); override;
  42.     procedure SetDateVariable(Index: Integer; const Value: TDateTime); override;
  43.     procedure SetIntegerVariable(Index: Integer; Value: Integer); override;
  44.     procedure SetLogMessage(const Value: string); override;
  45.     procedure SetStatusCode(Value: Integer); override;
  46.     procedure SetStringVariable(Index: Integer; const Value: string); override;
  47.   public
  48.     constructor Create(HTTPRequest: TWebRequest);
  49.     procedure SendResponse; override;
  50.     procedure SendRedirect(const URI: string); override;
  51.     procedure SendStream(AStream: TStream); override;
  52.   end;
  53.  
  54.   TWinCGIRequest = class(TCGIRequest)
  55.   private
  56.     FIniFile: TIniFile;
  57.     FClientData, FServerData: TFileStream;
  58.   protected
  59.     function GetStringVariable(Index: Integer): string; override;
  60.   public
  61.     constructor Create(IniFileName, ContentFile, OutputFile: string);
  62.     destructor Destroy; override;
  63.     function GetFieldByName(const Name: string): string; override;
  64.     function ReadClient(var Buffer; Count: Integer): Integer; override;
  65.     function ReadString(Count: Integer): string; override;
  66.     function TranslateURI(const URI: string): string; override;
  67.     function WriteClient(var Buffer; Count: Integer): Integer; override;
  68.     function WriteString(const AString: string): Boolean; override;
  69.   end;
  70.  
  71.   TWinCGIResponse = class(TCGIResponse);
  72.  
  73.   TCGIApplication = class(TWebApplication)
  74.   private
  75.     FOutputFileName: string;
  76.     function NewRequest: TCGIRequest;
  77.     function NewResponse(CGIRequest: TCGIRequest): TCGIResponse;
  78.   public
  79.     procedure Run; override;
  80.   end;
  81.  
  82. implementation
  83.  
  84. uses SysUtils, WebConst;
  85.  
  86. const
  87.   CGIServerVariables: array[0..28] of string = (
  88.     'REQUEST_METHOD',
  89.     'SERVER_PROTOCOL',
  90.     'URL',
  91.     'QUERY_STRING',
  92.     'PATH_INFO',
  93.     'PATH_TRANSLATED',
  94.     'HTTP_CACHE_CONTROL',
  95.     'HTTP_DATE',
  96.     'HTTP_ACCEPT',
  97.     'HTTP_FROM',
  98.     'HTTP_HOST',
  99.     'HTTP_IF_MODIFIED_SINCE',
  100.     'HTTP_REFERER',
  101.     'HTTP_USER_AGENT',
  102.     'HTTP_CONTENT_ENCODING',
  103.     'HTTP_CONTENT_TYPE',
  104.     'HTTP_CONTENT_LENGTH',
  105.     'HTTP_CONTENT_VERSION',
  106.     'HTTP_DERIVED_FROM',
  107.     'HTTP_EXPIRES',
  108.     'HTTP_TITLE',
  109.     'REMOTE_ADDR',
  110.     'REMOTE_HOST',
  111.     'SCRIPT_NAME',
  112.     'SERVER_PORT',
  113.     '',
  114.     'HTTP_CONNECTION',
  115.     'HTTP_COOKIE',
  116.     'HTTP_AUTHORIZATION');
  117.  
  118. { TCGIRequest }
  119.  
  120. constructor TCGIRequest.Create;
  121. begin
  122.   inherited Create;
  123.   FContent := ReadString(ContentLength);
  124. end;
  125.  
  126. function TCGIRequest.GetFieldByName(const Name: string): string;
  127. var
  128.   Buffer: array[0..4095] of Char;
  129.  
  130.   function StripHTTP(const Name: string): string;
  131.   begin
  132.     if Pos('HTTP_', Name) = 1 then
  133.       Result := Copy(Name, 6, MaxInt)
  134.     else Result := Name;
  135.   end;
  136.  
  137. begin
  138.   SetString(Result, Buffer, GetEnvironmentVariable(PChar(Name), Buffer, SizeOf(Buffer)));
  139.   if Result = '' then
  140.     SetString(Result, Buffer, GetEnvironmentVariable(PChar(StripHTTP(Name)), Buffer, SizeOf(Buffer)));
  141. end;
  142.  
  143. function TCGIRequest.GetStringVariable(Index: Integer): string;
  144. begin
  145.   if Index = 25 then
  146.     Result := FContent
  147.   else Result := GetFieldByName(CGIServerVariables[Index]);
  148. end;
  149.  
  150. function TCGIRequest.GetDateVariable(Index: Integer): TDateTime;
  151. var
  152.   Value: string;
  153. begin
  154.   Value := GetStringVariable(Index);
  155.   if Value <> '' then
  156.     Result := ParseDate(Value)
  157.   else Result := -1;
  158. end;
  159.  
  160. function TCGIRequest.GetIntegerVariable(Index: Integer): Integer;
  161. var
  162.   Value: string;
  163. begin
  164.   Value := GetStringVariable(Index);
  165.   Result := StrToIntDef(Value, -1)
  166. end;
  167.  
  168. function TCGIRequest.ReadClient(var Buffer; Count: Integer): Integer;
  169. begin
  170.   Result := FileRead(TTextRec(Input).Handle, Buffer, Count);
  171. end;
  172.  
  173. function TCGIRequest.ReadString(Count: Integer): string;
  174. begin
  175.   SetLength(Result, Count);
  176.   if Count > 0 then
  177.     FileRead(TTextRec(Input).Handle, Pointer(Result)^, Count);
  178. end;
  179.  
  180. function TCGIRequest.TranslateURI(const URI: string): string;
  181. begin
  182. end;
  183.  
  184. function TCGIRequest.WriteClient(var Buffer; Count: Integer): Integer;
  185. begin
  186.   Result := FileWrite(TTextRec(Output).Handle, Buffer, Count);
  187. end;
  188.  
  189. function TCGIRequest.WriteString(const AString: string): Boolean;
  190. begin
  191.   if AString <> '' then
  192.     Result := FileWrite(TTextRec(Output).Handle, Pointer(AString)^, Length(AString)) = Length(AString)
  193.   else Result := False;
  194. end;
  195.  
  196. { TCGIResponse }
  197.  
  198. constructor TCGIResponse.Create(HTTPRequest: TWebRequest);
  199. begin
  200.   inherited Create(HTTPRequest);
  201.   if FHTTPRequest.ProtocolVersion = '' then
  202.     Version := '1.0';
  203.   StatusCode := 200;
  204.   LastModified := -1;
  205.   Expires := -1;
  206.   Date := -1;
  207.   ContentType := 'text/html';
  208. end;
  209.  
  210. function TCGIResponse.GetContent: string;
  211. begin
  212.   Result := FContent;
  213. end;
  214.  
  215. function TCGIResponse.GetDateVariable(Index: Integer): TDateTime;
  216. begin
  217.   if (Index >= 0) and (Index < 3) then
  218.     Result := FDateVariables[Index]
  219.   else Result := -1;
  220. end;
  221.  
  222. function TCGIResponse.GetIntegerVariable(Index: Integer): Integer;
  223. begin
  224.   if (Index >= 0) and (Index < 2) then
  225.     Result := FIntegerVariables[Index]
  226.   else Result := -1;
  227. end;
  228.  
  229. function TCGIResponse.GetLogMessage: string;
  230. begin
  231. //  Result := TCGIRequest(HTTPRequest).ECB.lpszLogData;
  232. end;
  233.  
  234. function TCGIResponse.GetStatusCode: Integer;
  235. begin
  236.   Result := FStatusCode;
  237. end;
  238.  
  239. function TCGIResponse.GetStringVariable(Index: Integer): string;
  240. begin
  241.   if (Index >= 0) and (Index < 12) then
  242.     Result := FStringVariables[Index];
  243. end;
  244.  
  245. function TCGIResponse.Sent: Boolean;
  246. begin
  247.   Result := FSent;
  248. end;
  249.  
  250. procedure TCGIResponse.SetContent(const Value: string);
  251. begin
  252.   FContent := Value;
  253.   ContentLength := Length(FContent);
  254. end;
  255.  
  256. procedure TCGIResponse.SetDateVariable(Index: Integer; const Value: TDateTime);
  257. begin
  258.   if (Index >= Low(FDateVariables)) and (Index <= High(FDateVariables)) then
  259.     if Value <> FDateVariables[Index] then
  260.       FDateVariables[Index] := Value;
  261. end;
  262.  
  263. procedure TCGIResponse.SetIntegerVariable(Index: Integer; Value: Integer);
  264. begin
  265.   if (Index >= Low(FIntegerVariables)) and (Index <= High(FIntegerVariables)) then
  266.     if Value <> FDateVariables[Index] then
  267.       FIntegerVariables[Index] := Value;
  268. end;
  269.  
  270. procedure TCGIResponse.SetLogMessage(const Value: string);
  271. begin
  272. //  StrPLCopy(TCGIRequest(HTTPRequest).ECB.lpszLogData, Value, HSE_LOG_BUFFER_LEN);
  273. end;
  274.  
  275. procedure TCGIResponse.SetStatusCode(Value: Integer);
  276. begin
  277.   if FStatusCode <> Value then
  278.   begin
  279.     FStatusCode := Value;
  280.     ReasonString := StatusString(Value);
  281.   end;
  282. end;
  283.  
  284. procedure TCGIResponse.SetStringVariable(Index: Integer; const Value: string);
  285. begin
  286.   if (Index >= Low(FStringVariables)) and (Index <= High(FStringVariables)) then
  287.     FStringVariables[Index] := Value;
  288. end;
  289.  
  290. procedure TCGIResponse.SendResponse;
  291. var
  292.   StatusString: string;
  293.   Headers: string;
  294.  
  295.   procedure AddHeaderItem(const Item, FormatStr: string);
  296.   begin
  297.     if Item <> '' then
  298.       Headers := Headers + Format(FormatStr, [Item]);
  299.   end;
  300.  
  301. begin
  302.   if HTTPRequest.ProtocolVersion <> '' then
  303.   begin
  304.     if (ReasonString <> '') and (StatusCode > 0) then
  305.       StatusString := Format('%d %s', [StatusCode, ReasonString])
  306.     else StatusString := '200 OK';
  307.     AddHeaderItem(StatusString, 'Status: %s'#13#10);
  308.     AddHeaderItem(Allow, 'Allow: %s'#13#10);
  309.     AddHeaderItem(SetCookie, 'Set-Cookie: %s'#13#10);
  310.     AddHeaderItem(DerivedFrom, 'Derived-From: %s'#13#10);
  311.     if Expires > 0 then
  312.       Headers := Headers +
  313.         FormatDateTime('"Expires: "' + DateFormat + ' "GMT"'#13#10, Expires);
  314.     if LastModified > 0 then
  315.       Headers := Headers +
  316.         FormatDateTime('"Last-Modified: "' + DateFormat + ' "GMT"'#13#10, LastModified);
  317.     AddHeaderItem(Title, 'Title: %s'#13#10);
  318.     AddHeaderItem(WWWAuthenticate, 'WWW-Authenticate: %s'#13#10);
  319.     AddCustomHeaders(Headers);
  320.     AddHeaderItem(ContentVersion, 'Content-Version: %s'#13#10);
  321.     AddHeaderItem(ContentEncoding, 'Content-Encoding: %s'#13#10);
  322.     AddHeaderItem(ContentType, 'Content-Type: %s'#13#10);
  323.     if (Content <> '') or (ContentStream <> nil) then
  324.       AddHeaderItem(IntToStr(ContentLength), 'Content-Length: %s'#13#10);
  325.     Headers := Headers + 'Content:'#13#10#13#10;
  326.     HTTPRequest.WriteString(Headers);
  327.   end;
  328.   if ContentStream = nil then
  329.     HTTPRequest.WriteString(Content)
  330.   else if ContentStream <> nil then
  331.   begin
  332.     SendStream(ContentStream);
  333.     ContentStream := nil; // Drop the stream
  334.   end;
  335.   FSent := True;
  336. end;
  337.  
  338. procedure TCGIResponse.SendRedirect(const URI: string);
  339. begin
  340.   HTTPRequest.WriteString(Format('Location: %s', [URI]));
  341.   FSent := True;
  342. end;
  343.  
  344. procedure TCGIResponse.SendStream(AStream: TStream);
  345. var
  346.   Buffer: array[0..8191] of Byte;
  347.   BytesToSend: Integer;
  348. begin
  349.   while AStream.Position < AStream.Size do
  350.   begin
  351.     BytesToSend := AStream.Read(Buffer, SizeOf(Buffer));
  352.     FHTTPRequest.WriteClient(Buffer, BytesToSend);
  353.   end;
  354. end;
  355.  
  356. const
  357.   WinCGIServerVariables: array[0..28] of string = (
  358.     'Request Method',
  359.     'Request Protocol',
  360.     'Url',
  361.     'Query String',
  362.     'Logical Path',
  363.     'Physical Path',
  364.     'Cache Control',
  365.     'Date',
  366.     'Accept',
  367.     'From',
  368.     'Host',
  369.     'If-Modified-Since',
  370.     'Referer',
  371.     'User-Agent',
  372.     'Content-Encoding',
  373.     'Content Type',
  374.     'Content Length',
  375.     'Content Version',
  376.     'Derived-From',
  377.     'Expires',
  378.     'Title',
  379.     'Remote Address',
  380.     'Remote Host',
  381.     'Executable Path',
  382.     'Server Port',
  383.     '',
  384.     'Connection',
  385.     'Cookie',
  386.     'Authorization');
  387.  
  388. { TWinCGIRequest }
  389.  
  390. constructor TWinCGIRequest.Create(IniFileName, ContentFile, OutputFile: string);
  391. begin
  392.   FIniFile := TIniFile.Create(IniFileName);
  393.   if ContentFile = '' then
  394.     ContentFile := FIniFile.ReadString('System', 'Content File', '');
  395.   if OutputFile = '' then
  396.     OutputFile := FIniFile.ReadString('System', 'Output File', '');
  397.   FClientData := TFileStream.Create(ContentFile, fmOpenRead or fmShareDenyNone);
  398.   FServerData := TFileStream.Create(OutputFile, fmOpenWrite or fmShareDenyNone);
  399.   inherited Create;
  400. end;
  401.  
  402. destructor TWinCGIRequest.Destroy;
  403. begin
  404.   FIniFile.Free;
  405.   FClientData.Free;
  406.   FServerData.Free;
  407.   inherited Destroy;
  408. end;
  409.  
  410. function TWinCGIRequest.GetFieldByName(const Name: string): string;
  411. begin
  412.   Result := FIniFile.ReadString('Extra Headers', Name, '');
  413. end;
  414.  
  415. function TWinCGIRequest.GetStringVariable(Index: Integer): string;
  416.  
  417.   function AcceptSection: string;
  418.   var
  419.     Section: TStringList;
  420.     I: Integer;
  421.   begin
  422.     Section := TStringList.Create;
  423.     try
  424.       FIniFile.ReadSection('Accept', Section);
  425.       Result := '';
  426.       for I := 0 to Section.Count - 1 do
  427.         Result := Result + Section[I] + ',';
  428.       if Result <> '' then SetLength(Result, Length(Result) - 1);
  429.     finally
  430.       Section.Free;
  431.     end;
  432.   end;
  433.  
  434. begin
  435.   case Index of
  436.     0..1,3..5,15..16,
  437.     21..24, 26..28:
  438.       Result := FIniFile.ReadString('CGI', WinCGIServerVariables[Index], '');
  439.     25: Result := FContent;
  440.     8: Result := AcceptSection;
  441.   else
  442.     if (Index >= Low(WinCGIServerVariables)) and (Index <= High(WinCGIServerVariables)) then
  443.       Result := GetFieldByName(WinCGIServerVariables[Index])
  444.     else Result := '';
  445.   end;
  446. end;
  447.  
  448. function TWinCGIRequest.ReadClient(var Buffer; Count: Integer): Integer;
  449. begin
  450.   Result := FClientData.Read(Buffer, Count);
  451. end;
  452.  
  453. function TWinCGIRequest.ReadString(Count: Integer): string;
  454. begin
  455.   SetLength(Result, Count);
  456.   if Count > 0 then
  457.     FClientData.Read(Pointer(Result)^, Count);
  458. end;
  459.  
  460. function TWinCGIRequest.TranslateURI(const URI: string): string;
  461. begin
  462. end;
  463.  
  464. function TWinCGIRequest.WriteClient(var Buffer; Count: Integer): Integer;
  465. begin
  466.   Result := FServerData.Write(Buffer, Count);
  467. end;
  468.  
  469. function TWinCGIRequest.WriteString(const AString: string): Boolean;
  470. begin
  471.   if AString <> '' then
  472.     Result := FServerData.Write(Pointer(AString)^, Length(AString)) = Length(AString)
  473.   else Result := False;
  474. end;
  475.  
  476. { TCGIApplication }
  477.  
  478. procedure HandleServerException(E: Exception; const OutputFile: string);
  479. var
  480.   ResultText, ResultHeaders: string;
  481.   OutFile: TStream;
  482. begin
  483.   ResultText := Format(sInternalServerError, [E.ClassName, E.Message]);
  484.   ResultHeaders := Format(
  485.     'Status: 500 %s'#13#10+               //Not resourced
  486.     'Content-Type: text/html'#13#10 +     //Not resourced
  487.     'Content-Length: %d'#13#10 +          //Not resourced
  488.     'Content:'#13#10#13#10, [E.Message, Length(ResultText)]); //Not resourced
  489.   if IsConsole then
  490.   begin
  491.     FileWrite(TTextRec(Output).Handle, Pointer(ResultHeaders)^, Length(ResultHeaders));
  492.     FileWrite(TTextRec(Output).Handle, Pointer(ResultText)^, Length(ResultText));
  493.   end else
  494.   begin
  495.     OutFile := TFileStream.Create(OutputFile, fmOpenWrite or fmShareDenyNone);
  496.     try
  497.       OutFile.Write(Pointer(ResultHeaders)^, Length(ResultHeaders));
  498.       OutFile.Write(Pointer(ResultText)^, Length(ResultText));
  499.     finally
  500.       OutFile.Free;
  501.     end;
  502.   end;
  503. end;
  504.  
  505. function TCGIApplication.NewRequest: TCGIRequest;
  506. var
  507.   Buffer: array[0..MAX_PATH] of Char;
  508. begin
  509.   if IsConsole then
  510.     Result := TCGIRequest.Create
  511.   else
  512.   begin
  513.     Result := TWinCGIRequest.Create(ParamStr(1), ParamStr(2), ParamStr(3));
  514.     FOutputFileName := ParamStr(3);
  515.     if FOutputFileName = '' then
  516.       SetString(FOutputFileName, Buffer, GetPrivateProfileString('System',
  517.         'Output File', '', Buffer, SizeOf(Buffer), PChar(ParamStr(1))));
  518.   end;
  519. end;
  520.  
  521. function TCGIApplication.NewResponse(CGIRequest: TCGIRequest): TCGIResponse;
  522. begin
  523.   if IsConsole then
  524.     Result := TCGIResponse.Create(CGIRequest)
  525.   else Result := TWinCGIResponse.Create(CGIRequest);
  526. end;
  527.  
  528. procedure TCGIApplication.Run;
  529. var
  530.   HTTPRequest: TCGIRequest;
  531.   HTTPResponse: TCGIResponse;
  532. begin
  533.   inherited Run;
  534.   if IsConsole then
  535.   begin
  536.     Rewrite(Output);
  537.     Reset(Input);
  538.   end;
  539.   try
  540.     HTTPRequest := NewRequest;
  541.     try
  542.       HTTPResponse := NewResponse(HTTPRequest);
  543.       try
  544.         HandleRequest(HTTPRequest, HTTPResponse);
  545.       finally
  546.         HTTPResponse.Free;
  547.       end;
  548.     finally
  549.       HTTPRequest.Free;
  550.     end;
  551.   except
  552.     HandleServerException(Exception(ExceptObject), FOutputFileName);
  553.   end;
  554. end;
  555.  
  556. procedure InitApplication;
  557. begin
  558.   Application := TCGIApplication.Create(nil);
  559. end;
  560.  
  561. procedure DoneApplication;
  562. begin
  563.   Application.Free;
  564.   Application := nil;
  565. end;
  566.  
  567. initialization
  568.   InitApplication;
  569. finalization
  570.   DoneApplication;
  571. end.
  572.  
  573.